jQuery.markmeditor = {
    langs: {
        zh: {
            bold: '加粗',
            italic: '斜体',
            createLink: '链接',
            blockquote: '引用',
            preCode: '代码',
            insertImage: '图片',
            insertOrderedList: '数字列表',
            insertUnorderedList: '普通列表',
            h2: '标题',
            insertHorizontalRule: '分割线',
            undo: '撤销',
            redo: '重做',
            help: 'MarkDown语法',
            preview: '编辑模式',
            live: '实时预览',
            edit: '预览模式',
            fullscreen: '全屏'
        }
    }
}

;(function ($) {
    $.fn.extend({
        markmeditor: function (options) {
            return new MarkMeditor(this, options || {});
        }
    })
})(jQuery);

/**
 * MarkMeditor对象
 * @param el
 * @param options
 * @constructor
 */
var MarkMeditor = function (el, options) {
    el = $(el)[0];      //选中被渲染的textarea的DOM
    this.element = el;
    var _this = this;

    //拖动改变大小
    this.staticOffset = 0, this.iLastMousePos = 0, this.iMin = 32;
    this.hashto;

    // 当前编辑区域
    //this.currentEditArea = undefined;

    if (typeof options.lang === 'undefined' || typeof $.markmeditor.langs[options.lang] === 'undefined') {
        this.lang = $.markmeditor.langs.zh;
    } else {
        this.lang = $.extend(true, {}, $.markmeditor.langs.zh, $.markmeditor.langs[options.lang]);
    }

    this.options = $.extend(true, {}, {
        // 国际化
        lang: 'zh',
        // 编辑器默认高度
        height: 300,
        // 图片上传请求地址
        uploadPath: '/',
        // 工具栏定位顶部高度
        toolbarFixedTop: 0,
        // 编辑器默认实时预览编辑模式
        mode: 'live',
        // CodeMirror编辑器主题
        theme: 'base16-light',
        // 开启内容哈希编码到地址栏，单页多编辑器实例请关闭
        useHash: false,
        // 初始化是否全屏
        fullscreen: false,

        // 是否允许 html 标签
        html: true,
        // Enter 换行生成 br 标签
        breaks: true,
        // 文本中 url 自动识别
        linkify: true,

        // 是否启用插件
        useMarkdownitAbbr: true,
        useMarkdownitContainer: true,
        useMarkdownitDeflist: true,
        useMarkdownitEmoji: true,
        useMarkdownitFootnote: true,
        useMarkdownitIns: true,
        useMarkdownitMark: true,
        useMarkdownitSub: true,
        useMarkdownitSup: true,
        useMarkdownitTaskLists: true,

        btnsLeft: {
            bold: {
                func: toggleBold,
                key: 'B',
                tag: '<strong>',
                self: 'bold'
            },
            italic: {
                func: toggleItalic,
                key: 'I',
                tag: '<em>',
                self: 'italic'
            },
            createLink: {
                func: toggleLink,
                key: 'L',
                tag: '<a>',
                self: 'createLink'
            },
            blockquote: {
                func: toggleBlockquote,
                key: 'Q',
                tag: '<blockquote>',
                self: 'blockquote'
            },
            preCode: {
                func: togglePreCode,
                key: 'K',
                tag: '<pre><code>',
                self: 'preCode'
            },
            insertImage: {
                func: toggleImage,
                key: 'G',
                tag: '<img>',
                self: 'insertImage'
            },
            insertOrderedList: {
                func: toggleOrderedList,
                key: 'O',
                tag: '<ol>',
                self: 'insertOrderedList'
            },
            insertUnorderedList: {
                func: toggleUnorderedList,
                key: 'U',
                tag: '<ul>',
                self: 'insertUnorderedList'
            },
            h2: {
                func: toggleHeaderLevel2,
                key: 'H',
                tag: '<h2>',
                self: 'h2'
            },
            insertHorizontalRule: {
                func: toggleHorizontalRule,
                key: 'R',
                tag: '<hr>',
                self: 'insertHorizontalRule'
            },
            undo: {
                func: undo,
                key: 'Z',
                tag: '-',
                self: 'undo'
            },
            redo: {
                func: redo,
                key: 'Y',
                tag: '-',
                self: 'redo'
            },
            help: {
                func: help,
                self: 'help'
            }
        },
        btnsRight: {
            preview: {
                func: toPreview,
                self: 'preview'
            },
            live: {
                func: tolive,
                self: 'live'
            },
            edit: {
                func: toEdit,
                self: 'edit'
            },
            fullscreen: {
                func: toggleFullScreen,
                self: 'fullscreen'
            }
        },
        btnsLeftAdd: {},
        btnsRightAdd: {}
    }, options);

    var keyMaps = {};
    for (var key in shortcuts) {
        (function(key) {
            keyMaps[fixShortcut(key)] = function() {
                shortcuts[key](_this);
            };
        })(key);
    }
    // 修改CodeMirror默认配置
    keyMaps['Enter'] = 'newlineAndIndentContinueMarkdownList';
    keyMaps['Shift-Tab'] = "indentLess";

    // 渲染codemirror
    this.codemirror = CodeMirror.fromTextArea(el, {
        mode: 'gfm',
        theme: _this.options.theme,
        indentUnit: 4,
        lineNumbers: true,
        lineWrapping: true,
        extraKeys: keyMaps,
        cursorBlinkRate: 500,
        cursorHeight: 0.85,
        viewportMargin: Infinity
    });
    this.textarea = $('.CodeMirror');    //要改变大小的codemirror

    this.init();
}

MarkMeditor.prototype = {
    init: function () {
        this.buildEditor();
        this.builToolBar();
        this.buildToolBarRight();
        this.buildDone();
        this.addCurrentEditAreaListener();
    },

    /**
     * 构建编辑器
     */
    buildEditor: function () {
        // 最外层包裹
        this.markmeditorWrapper = $("<div>").addClass('markmeditor').attr({
            'style': 'min-height: ' + this.options.height + 'px'
        });

        // 左边包裹 CodeMirror 编辑器
        this.codemirrorWrapper = $('<div>').addClass('markmeditor-codemirror-wrapper');
        this.codemirrorWrapper.insertAfter(this.textarea).append(this.textarea);
        this.markmeditorWrapper.insertAfter(this.codemirrorWrapper).append(this.codemirrorWrapper);

        // 分割线
        this.editorLine = $('<div>').addClass('editor-line');
        this.markmeditorWrapper.append(this.editorLine);

        // 右边 HTML 预览区
        this.htmlDiv = $('<div>').addClass('markmeditor-previewer').html('').attr({
            'id': 'html-div-' + this.element.getAttribute('id')
        });
        this.markmeditorWrapper.append(this.htmlDiv);

        this.markmeditorWrapper.after($("<a>").addClass('editor__resize').attr({
            'href': 'javascript:void(0);'
        }).text('调整高度'));
    },

    /**
     * 构建工具栏
     */
    builToolBar: function () {
        this.toolBarWrapper = $('<div>').addClass('markmeditor-toolbar');
        this.markmeditorWrapper.prepend(this.toolBarWrapper);
        this.buildToolBarLeft();
    },

    /**
     * 构建工具栏左边菜单
     */
    buildToolBarLeft: function () {
        var btnsLeft = {},
            lang = this.lang,
            options = this.options,
            _this = this;
        var toolBarLeftMenu = $('<ul>').addClass('tb-menu d-none d-md-block').attr({
            'position': 'static',
            'width': 'auto',
            'border-bottom-style': 'none',
            'top': 0,
            'z-index': 1000
        });
        $.each($.extend(btnsLeft, this.options.btnsLeft, this.options.btnsLeftAdd), function (i, btn) {
            var li = $('<li>').addClass('markme-button').attr({
                'id': 'markme' + btn.self + 'button',
                'title': lang[btn.self] + (typeof btn.tag === 'undefined' ? '' : ' ' + btn.tag) 
                + (typeof btn.key === 'undefined' ? '' : ' ' 
                    + (/Mac/.test(navigator.platform) ? 'Cmd' : 'Ctrl') 
                    + '+' + btn.key)
            });
            var button = $('<button>').attr({'type': 'button'}).addClass('tb-menu-' + btn.self);
            li.append(button);
            if (btn.func) {
                if (typeof btn.func === 'function') {
                    li.click(function () {
                        _this.textarea.focus();
                        btn.func(_this);
                    });
                }
            }
            toolBarLeftMenu.append(li);
        });
        this.toolBarWrapper.append(toolBarLeftMenu);
    },

    /**
     * 构建工具栏右边菜单
     */
    buildToolBarRight: function () {
        var btnsRight = {},
            lang = this.lang,
            options = this.options,
            _this = this;
        var toolBarRightMenu = $('<ul>').addClass('tb-mode');
        $.each($.extend(btnsRight, this.options.btnsRight, this.options.btnsRightAdd), function (i, btn) {
            var li = $('<li>').addClass('pull-right');
            var button = $('<button>').addClass('tb-menu-' + btn.self).attr({
                'title': lang[btn.self],
                'type': 'button'
            });
            li.append(button);
            // 注册事件
            if (btn.func) {
                if (typeof btn.func === 'function') {
                    li.click(function () {
                        btn.func(_this);
                    });
                }
            }
            toolBarRightMenu.append(li);
        });
        this.toolBarWrapper.prepend(toolBarRightMenu);
    },

    buildDone: function () {
        var _this = this;
        // 最后默认开启live实时预览模式
        if (_this.options.mode === 'live') {
            _this.markmeditorWrapper.find('.tb-menu-live').trigger('click');
        } else if (_this.options.mode === 'edit') {
            _this.markmeditorWrapper.find('.tb-menu-edit').trigger('click');
        } else if (_this.options.mode === 'preview') {
            _this.markmeditorWrapper.find('.tb-menu-preview').trigger('click');
        } else {
            _this.markmeditorWrapper.find('.tb-menu-live').trigger('click');
        }

        // 工具栏fixed
        $(window).scroll(function () {
            if (!_this.options.fullscreen) {
                // 编辑器宽度
                var _width = _this.markmeditorWrapper.width();
                // 编辑器距离页面顶部的高度
                var _top = _this.markmeditorWrapper.offset().top;
                var _top_top = _top - _this.options.toolbarFixedTop;
                var _top_bottom = _top_top + _this.options.height;
                // 当前滚动条的位置
                var _scrollTop = $(this).scrollTop();
                if (_scrollTop >= _top_top && _scrollTop <= _top_bottom) {
                    _this.markmeditorWrapper.find('.markmeditor-toolbar').css({
                        'position': 'fixed',
                        'top': _this.options.toolbarFixedTop,
                        'width': _width
                    });
                } else {
                    _this.markmeditorWrapper.find('.markmeditor-toolbar').removeAttr('style');
                }
            }
        });

        // 编辑器拖拽缩放
        _this.markmeditorWrapper.next('.editor__resize').on('mousedown', function(e) {
            _this.iLastMousePos = _this.mousePosition(e).y;
            _this.staticOffset = _this.markmeditorWrapper.height() - _this.iLastMousePos;
            _this.markmeditorWrapper.css('opacity', 0.3);

            $(document).mousemove(_this, _this.performDrag).mouseup(_this, _this.endDrag);
            return false;
        });

        // 同步滚动，需要配置
        $(".CodeMirror-scroll").scroll(function() {
            _this.htmlDiv.scrollTop($(this).scrollTop()); // 纵向滚动条
            _this.htmlDiv.scrollLeft($(this).scrollLeft()); // 横向滚动条
        });

        // hash url decode
        if (_this.options.useHash) {
            if(window.location.hash){
                var h = window.location.hash.replace(/^#/, '');
                if(h.slice(0,5) == 'view:'){
                    _this.setHtml(decodeURIComponent(escape(RawDeflate.inflate(atob(h.slice(5))))));
                    document.body.className = 'view';
                } else {
                    _this.setValue(
                        decodeURIComponent(escape(
                            RawDeflate.inflate(
                                atob(
                                    h
                                )
                            )
                        ))
                    );
                    _this.syncCode();
                    _this.codemirror.focus();
                }
            }else{
                _this.syncCode();
                _this.codemirror.focus();
            }
        }
    },

    // 拖拽开始
    performDrag: function(e) {
        var _this = e.data;
        var iThisMousePos = _this.mousePosition(e).y,
            iMousePos = _this.staticOffset + iThisMousePos;
        if (_this.iLastMousePos >= (iThisMousePos)) {
            iMousePos -= 5;
        }

        _this.iLastMousePos = iThisMousePos;
        iMousePos = Math.max(_this.iMin, iMousePos);
        _this.markmeditorWrapper.height(iMousePos + 'px');

        if (iMousePos < _this.iMin) {
            endDrag(e);
        }
        return false;
    },

    endDrag: function(e) {
        var _this = e.data;
        $(document).unbind('mousemove', _this.performDrag).unbind('mouseup', _this.endDrag);
        var editor = _this.markmeditorWrapper;
        editor.css('opacity', 1);
        editor.focus();
        editor = null;

        _this.staticOffset = null;
        _this.iLastMousePos = 0;
    },

    mousePosition: function(e) {
        return { x: e.clientX + document.documentElement.scrollLeft, y: e.clientY + document.documentElement.scrollTop };
    },

    /**
     * 快捷键
     */
    addCurrentEditAreaListener: function () {
        var _this = this;
        // 编辑MarkDown
        _this.codemirror.on('change', function() {
            _this.syncCode();
        });
    },

    /**
     * 获取MarkDown值
     * @returns {*}
     */
    getValue: function () {
        return this.codemirror.getValue();
    },

    /**
     * 设置MarkDown值
     */
    setValue: function (text) {
        return this.codemirror.setValue(text);
    },

    /**
     * 获取HTML值
     * @returns {*}
     */
    getHtml: function () {
        var _this = this;
        // return this.htmlDiv.html();
        // no highlight and security more!!!
        var md = markdownit({
            html: _this.options.html,
            breaks: _this.options.breaks,
            linkify: _this.options.linkify
        });

        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "primary"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "secondary"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "success"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "danger"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "warning"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "info"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "light"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "dark"); }
        if (_this.options.useMarkdownitContainer) {
            md.use(markdownitContainer, 'download', {
                validate: function(params) {
                    return params.trim().match(/^download\s+(.*)$/);
                },
                render: function (tokens, idx) {
                    var m = tokens[idx].info.trim().match(/^download\s+(.*)$/);
              
                    if (tokens[idx].nesting === 1) {
                        // opening tag
                        return '<div class="markmeditor-download"><a href="' + md.utils.escapeHtml(m[1])
                        + '" target="_blank" rel="inlinks" style="background: none; padding: 0px;">'
                        + '<img src="/img/down.png" class="markmeditor-download-icon"></a>\n';
                    } else {
                        // closing tag
                        return '</div>\n';
                    }
                }
            });
        }

        if (_this.options.useMarkdownitAbbr)      { md.use(markdownitAbbr); }
        if (_this.options.useMarkdownitDeflist)   { md.use(markdownitDeflist); }
        if (_this.options.useMarkdownitEmoji)     { md.use(markdownitEmoji); }
        if (_this.options.useMarkdownitFootnote)  { md.use(markdownitFootnote); }
        if (_this.options.useMarkdownitIns)       { md.use(markdownitIns); }
        if (_this.options.useMarkdownitMark)      { md.use(markdownitMark); }
        if (_this.options.useMarkdownitSub)       { md.use(markdownitSub); }
        if (_this.options.useMarkdownitSup)       { md.use(markdownitSup); }
        if (_this.options.useMarkdownitTaskLists) { md.use(markdownitTaskLists); }

        return md.render(this.getValue());
    },

    /**
     * 设置HTML值
     */
    setHtml: function (html) {
        return this.htmlDiv.html(html);
    },

    updateHash: function(_this) {
        window.location.hash = btoa( // base64 so url-safe
            RawDeflate.deflate( // gzip
                unescape(encodeURIComponent(_this.getValue()))
            )
        );
    },

    /**
     * 同步
     */
    syncCode: function () {
        var _this = this;

        var languageOverrides = {
            js: 'javascript',
            html: 'xml'
        };

        var md = markdownit({
            html: _this.options.html,
            breaks: _this.options.breaks,
            linkify: _this.options.linkify,
            highlight: function(code, lang){
                if(languageOverrides[lang]) lang = languageOverrides[lang];
                if(lang && hljs.getLanguage(lang)){
                    try {
                        return hljs.highlight(lang, code).value;
                    } catch(e) {}
                } else {
                    try {
                        return hljs.highlightAuto(code).value;
                    } catch(e) {}
                }
                return '';
            }
        });

        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "primary"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "secondary"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "success"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "danger"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "warning"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "info"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "light"); }
        if (_this.options.useMarkdownitContainer) { md.use(markdownitContainer, "dark"); }
        if (_this.options.useMarkdownitContainer) {
            md.use(markdownitContainer, 'download', {
                validate: function(params) {
                    return params.trim().match(/^download\s+(.*)$/);
                },
                render: function (tokens, idx) {
                    var m = tokens[idx].info.trim().match(/^download\s+(.*)$/);
              
                    if (tokens[idx].nesting === 1) {
                        // opening tag
                        return '<div class="markmeditor-download"><a href="' + md.utils.escapeHtml(m[1])
                        + '" target="_blank" rel="inlinks" style="background: none; padding: 0px;">'
                        + '<img src="/img/down.png" class="markmeditor-download-icon"></a>\n';
                    } else {
                        // closing tag
                        return '</div>\n';
                    }
                }
            });
        }

        if (_this.options.useMarkdownitAbbr)      { md.use(markdownitAbbr); }
        if (_this.options.useMarkdownitDeflist)   { md.use(markdownitDeflist); }
        if (_this.options.useMarkdownitEmoji)     { md.use(markdownitEmoji); }
        if (_this.options.useMarkdownitFootnote)  { md.use(markdownitFootnote); }
        if (_this.options.useMarkdownitIns)       { md.use(markdownitIns); }
        if (_this.options.useMarkdownitMark)      { md.use(markdownitMark); }
        if (_this.options.useMarkdownitSub)       { md.use(markdownitSub); }
        if (_this.options.useMarkdownitSup)       { md.use(markdownitSup); }
        if (_this.options.useMarkdownitTaskLists) { md.use(markdownitTaskLists); }

        var codemirrorValue = _this.codemirror.getValue().replace(/\t/g, '    ')
            .replace(/<\s*?script/ig, '&lt;script')
            .replace(/<\s*?\/\s*?script\s*?>/ig, '&lt;/script&gt;');
        
        _this.htmlDiv.html(md.render(codemirrorValue));

        // hash url encode
        if (_this.options.useHash) {
            clearTimeout(_this.hashto);
            _this.hashto = setTimeout(_this.updateHash(_this), 1000);
        }

        console.log('word count: ' + this.wordCount(codemirrorValue));

        $(".CodeMirror-scroll").scroll();
    },

    wordCount: function (data) {
        var pattern = /[a-zA-Z0-9_\u0392-\u03c9]+|[\u4E00-\u9FFF\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\uac00-\ud7af]+/g;
        var m = data.match(pattern);
        var count = 0;
        if (m === null) {
            return count;
        }
        for (var i = 0; i < m.length; i++) {
            if (m[i].charCodeAt(0) >= 0x4E00) {
                count += m[i].length;
            } else {
                count += 1;
            }
        }
        return count;
    }
}

// prototype function function
MarkMeditor.prototype.toPreview = function () {toPreview(this);}
MarkMeditor.prototype.tolive = function () {tolive(this);}
MarkMeditor.prototype.toEdit = function () {toEdit(this);}
MarkMeditor.prototype.toggleFullScreen = function () {toggleFullScreen(this);}
MarkMeditor.prototype.toggleBold = function () {toggleBold(this);}
MarkMeditor.prototype.toggleItalic = function () {toggleItalic(this);}
MarkMeditor.prototype.toggleLink = function () {toggleLink(this);}
MarkMeditor.prototype.toggleBlockquote = function () {toggleBlockquote(this);}
MarkMeditor.prototype.togglePreCode = function () {togglePreCode(this);}
MarkMeditor.prototype.toggleImage = function () {toggleImage(this);}
MarkMeditor.prototype.toggleOrderedList = function () {toggleOrderedList(this);}
MarkMeditor.prototype.toggleUnorderedList = function () {toggleUnorderedList(this);}
MarkMeditor.prototype.toggleHeaderLevel2 = function () {toggleHeaderLevel2(this);}
MarkMeditor.prototype.toggleHorizontalRule = function () {toggleHorizontalRule(this);}
MarkMeditor.prototype.undo = function () {undo(this);}
MarkMeditor.prototype.redo = function () {redo(this);}
MarkMeditor.prototype.help = function () {help(this);}


var isMac = /Mac/.test(navigator.platform);

var shortcuts = {
    'Cmd-B': toggleBold,
    'Cmd-I': toggleItalic,
    'Cmd-L': toggleLink,
    'Cmd-G': toggleImage,
    'Cmd-Q': toggleBlockquote,
    'Cmd-K': togglePreCode,
    'Cmd-O': toggleOrderedList,
    'Cmd-U': toggleUnorderedList,
    'Cmd-Z': undo,
    'Cmd-Shift-Z': redo,
    'Cmd-R': toggleHorizontalRule,
    'Cmd-H': toggleHeaderLevel2,
    'Cmd-2': toggleHeaderLevel2,
    'Cmd-3': toggleHeaderLevel3,
    'Cmd-4': toggleHeaderLevel4,
    'Cmd-5': toggleHeaderLevel5,
    'Cmd-6': toggleHeaderLevel6,
    'F11'  : toggleFullScreen
};

/**
 * 兼容Mac上的Command快捷键
 */
function fixShortcut(name) {
    if (isMac) {
        name = name.replace('Ctrl', 'Cmd');
    } else {
        name = name.replace('Cmd', 'Ctrl');
    }
    return name;
}

/**
 * The state of CodeMirror at the given position.
 */
function getState(cm, pos) {
    pos = pos || cm.getCursor('start');
    var stat = cm.getTokenAt(pos);
    if (!stat.type) {    //没有markdown关键字
        return {};
    } else {
        var types = stat.type.split(' ');

        var ret = {}, data, text;
        for (var i = 0; i < types.length; i++) {
            data = types[i];
            if (data === 'strong') {
                ret.bold = true;
            } else if (data === 'variable-2') {
                text = cm.getLine(pos.line);
                if (/^\s*\d+\.\s/.test(text)) {
                    ret['ordered-list'] = true;
                } else {
                    ret['unordered-list'] = true;
                }
            } else if (data === 'quote') {
                ret.quote = true;
            } else if (data === 'em') {
                ret.italic = true;
            }
        }
        return ret;
    }
}

/**
 * 预览模式，只编辑HTML
 * @param editor MarkMeditor
 */
function toPreview(editor) {
    var _this = editor;
    _this.htmlDiv.attr({
        'style': 'width:100%; left: 0;'
    });
    _this.codemirrorWrapper.attr({
        'style': 'width:0;'
    });
    _this.editorLine.hide();
    // 变灰只编辑HTML按钮
    _this.markmeditorWrapper.find('.tb-menu-preview').addClass('muted');
    // 恢复实时预览和只编辑MarkDown按钮
    _this.markmeditorWrapper.find('.tb-menu-live').removeClass('muted');
    _this.markmeditorWrapper.find('.tb-menu-edit').removeClass('muted');
    _this.codemirror.refresh();
}

/**
 * 实时预览模式
 * @param editor MarkMeditor
 */
function tolive(editor) {
    var _this = editor;
    _this.codemirrorWrapper.attr({
        'style': 'width:50%;'
    });
    _this.htmlDiv.attr({
        'style': 'width:50%; left: 50%;'
    });
    _this.editorLine.show();
    // 变灰实时预览按钮
    _this.markmeditorWrapper.find('.tb-menu-live').addClass('muted');
    // 恢复只编辑MarkDown和只编辑HTML按钮
    _this.markmeditorWrapper.find('.tb-menu-edit').removeClass('muted');
    _this.markmeditorWrapper.find('.tb-menu-preview').removeClass('muted');
    _this.codemirror.refresh();
}

/**
 * 编辑模式，只编辑MarkDown
 * @param editor MarkMeditor
 */
function toEdit(editor) {
    var _this = editor;
    _this.codemirrorWrapper.attr({
        'style': 'width:100%;'
    });
    _this.htmlDiv.attr({
        'style': 'width:0; left: 100%;'
    });
    _this.editorLine.hide();
    // 变灰只编辑MarkDown按钮
    _this.markmeditorWrapper.find('.tb-menu-edit').addClass('muted');
    // 恢复实时预览和只编辑HTML按钮
    _this.markmeditorWrapper.find('.tb-menu-live').removeClass('muted');
    _this.markmeditorWrapper.find('.tb-menu-preview').removeClass('muted');
    _this.codemirror.refresh();
}

/**
 * 编辑器全屏切换
 * @param editor MarkMeditor
 */
function toggleFullScreen(editor) {
    var _this = editor;
    if (_this.markmeditorWrapper.find('.tb-menu-fullscreen-ing').length > 0) {
        // 退出全屏
        _this.markmeditorWrapper.removeClass('markme-editor-fullscreen');

        _this.markmeditorWrapper.find('.tb-menu-fullscreen-ing')
                                .removeClass('tb-menu-fullscreen-ing')
                                .addClass('tb-menu-fullscreen');
        _this.options.fullscreen = false;
        $('html').removeAttr('style');

        // 修正全屏前工具栏已经fixed，再次退出全屏后工具栏应该再次fixed
        // 编辑器宽度
        var _width = _this.markmeditorWrapper.width();

        // 编辑器距离页面顶部的高度
        var _top = _this.markmeditorWrapper.offset().top - _this.options.toolbarFixedTop;
        
        // 当前滚动条的位置
        var _scrollTop = $(window).scrollTop();
        if (_scrollTop >= _top) {
            $('.markmeditor-toolbar').css({
                'position': 'fixed',
                'top': _this.options.toolbarFixedTop,
                'width': _width
            });
        } else {
            $('.markmeditor-toolbar').removeAttr('style');
        }

    } else {
        // 全屏
        _this.markmeditorWrapper.addClass('markme-editor-fullscreen');
        _this.markmeditorWrapper.css({'height': '100%'});
        _this.markmeditorWrapper.find('.tb-menu-fullscreen')
                                .removeClass('tb-menu-fullscreen')
                                .addClass('tb-menu-fullscreen-ing');
        _this.options.fullscreen = true;
        // 隐藏浏览器滚动条
        $('html').css({'overflow': 'hidden'});
        // 修正全屏前工具栏已经fixed而不能置顶的问题
        _this.toolBarWrapper.removeAttr('style');
    }
    _this.codemirror.refresh();
}

/**
 * 文本加粗
 * @param editor MarkMeditor
 */
function toggleBold(editor) {
    var cm = editor.codemirror;
    var stat = getState(cm);

    var text = '加粗文字';
    var start = '**';
    var end = '**';
    var startPoint, endPoint, curPoint;
    if(cm.getSelection()) {   // 有选中
        startPoint = cm.getCursor('from');
        endPoint = cm.getCursor('to');
    } else {    //没有选中
        curPoint = cm.getCursor();
    }
    if (stat.bold) {
        if(curPoint || cm.getRange({line: startPoint.line, ch: startPoint.ch - 2} ,startPoint) !== '**' 
            || cm.getRange(endPoint, {line: endPoint.line, ch: endPoint.ch + 2}) !== '**') {
            // 没有选中或选中的不全，返回
            return;
        } else {
            text = cm.getSelection();
            startPoint.ch -= 2;
            endPoint.ch += 2;
            cm.replaceRange(text,
                startPoint,
                endPoint
            );
            endPoint.ch -= 4;
        }
    } else {
        var _text = cm.getSelection() || text;
        cm.replaceSelection(start + _text + end);
        if(curPoint) {
            startPoint = {line: curPoint.line, ch: curPoint.ch + 2};
            endPoint = {line: curPoint.line, ch: curPoint.ch + 6};
        } else {
            startPoint.ch += 2;
            endPoint.ch += 2;
        }
    }
    cm.setSelection(startPoint, endPoint);
    cm.focus();
}

/**
 * 文本斜体
 * @param editor MarkMeditor
 */
function toggleItalic(editor) {
    var cm = editor.codemirror;
    var stat = getState(cm);

    var text = '斜体文字';
    var start = '*';
    var end = '*';

    var startPoint, endPoint, curPoint;
    if(cm.getSelection()) {   // 有选中
        startPoint = cm.getCursor('from');
        endPoint = cm.getCursor('to');
    } else {    //没有选中
        curPoint = cm.getCursor();
    }
    if (stat.italic) {
        if(curPoint || cm.getRange({line: startPoint.line, ch: startPoint.ch - 1} ,startPoint) !== '*' 
            || cm.getRange(endPoint, {line: endPoint.line, ch: endPoint.ch + 1}) !== '*') {
            // 没有选中或选中的不全
            return;
        } else {
            text = cm.getSelection();
            startPoint.ch -= 1;
            endPoint.ch += 1;
            cm.replaceRange(text,
                startPoint,
                endPoint
            );
            endPoint.ch -= 2;
        }
    } else {
        var _text = cm.getSelection() || text;
        cm.replaceSelection(start + _text + end);
        if(curPoint) {
            startPoint = {line: curPoint.line, ch: curPoint.ch + 1};
            endPoint = {line: curPoint.line, ch: curPoint.ch + 5};
        } else {
            startPoint.ch += 1;
            endPoint.ch += 1;
        }
    }
    cm.setSelection(startPoint, endPoint);
    cm.focus();
}

/**
 * 超文本链接
 * @param editor MarkMeditor
 */
function toggleLink(editor) {
    var cm = editor.codemirror;
    
    var startPoint = cm.getCursor('start');
    var endPoint = cm.getCursor('end');

    var selectText = cm.getSelection();

    var linkValue = 'http://';
    var canAdditional = false;
    var linkValueLength = 0;
    if (cm.getRange({line: startPoint.line, ch: startPoint.ch - 1}, startPoint) === '['
     && cm.getRange(endPoint, {line: endPoint.line, ch: endPoint.ch + 1}) === ']'
     && cm.getRange({line: endPoint.line, ch: endPoint.ch + 1}, {line: endPoint.line, ch: endPoint.ch + 2}) === '(') {
        var currentLine = cm.getLine(startPoint.line);
        var currentLineLast = currentLine.substring(endPoint.ch);
        var indexL = currentLineLast.indexOf('(');
        var indexR = currentLineLast.indexOf(')');
        if (indexL > -1 && indexR > indexL) {
            linkValue = currentLineLast.substring(indexL + 1, indexR);
            canAdditional = true;
            linkValueLength = indexR - indexL - 1;
        }
    }

    oceModal({
        title: '超链接',
        content:
        '<form class="form-horizontal">'
        + '<div class="form-group">'
        +   '<label for="editorLinkTitle">标题</label>'
        +   '<input type="text" id="editorLinkTitle" class="form-control" placeholder="请输入链接标题" value="' + selectText + '">'
        + '</div>'
        + '<div class="form-group">'
        +   '<label for="editorLinkText">链接</label>'
        +   '<input type="text" id="editorLinkText" class="form-control" placeholder="请输入链接地址" value="' + linkValue + '">'
        + '</div>'
        + '</form>',
        closeText: '关闭',
        doneText: '生成链接',
        doneFn: function() {
            var editorLinkTitle = $('#editorLinkTitle').val().replace(/(^\s*)|(\s*$)/g, '');
            var editorLinkText = $('#editorLinkText').val().replace(/(^\s*)|(\s*$)/g, '');
            if (editorLinkTitle === '' || editorLinkText === '') {
                return;
            }
            var mdLinkText = '[' + editorLinkTitle + '](' + editorLinkText + ')';
            if (canAdditional) {
                cm.replaceRange(mdLinkText,
                    {line: startPoint.line, ch: startPoint.ch - 1},
                    {line: endPoint.line, ch: endPoint.ch + 1 + 1 + linkValueLength + 1}
                );
            } else {
                cm.replaceSelection(mdLinkText);
            }
            oceModal('hide');
            cm.focus();
        },
        hideAdditional: false,
        additionalText: '取消链接',
        additionalFn: function () {
            if (!canAdditional) {
                return;
            }
            cm.replaceRange(selectText,
                {line: startPoint.line, ch: startPoint.ch - 1},
                {line: endPoint.line, ch: endPoint.ch + 1 + 1 + linkValueLength + 1}
            );
            oceModal('hide');
            cm.focus();
        }
    });
}

/**
 * 代码块
 * @param editor MarkMeditor
 */
function togglePreCode(editor) {
    var cm = editor.codemirror;
    var startPoint = cm.getCursor('start');
    var endPoint = cm.getCursor('end');

    var ncode;
    if(cm.somethingSelected()) {
        var code = cm.getSelection();
        if (startPoint.line == endPoint.line) {
            ncode = '`' + code + '`';
            cm.replaceSelection(ncode);
        } else {
            ncode = '```\n' + code + '\n```\n';
            if (startPoint.line != 0) {
                ncode = '\n' + ncode;
            }
            cm.replaceSelection(ncode);
        }
    } else {
        var currentLineNumber = startPoint.line;
        var currentLine = cm.getLine(currentLineNumber);
        if (currentLine == '') {
            ncode = '```\n' + '请输入代码' + '\n```\n';
            currentLineNumber++;
            if (startPoint.line != 0) {
                ncode = '\n' + ncode;
                currentLineNumber++;
            }
            cm.replaceSelection(ncode);
            cm.setSelection(
                { line: currentLineNumber, ch: 0},
                { line: currentLineNumber, ch: 5}
            );
        } else {
            ncode = '`CODE`';
            cm.replaceSelection(ncode);
            cm.setSelection(
                { line: currentLineNumber, ch: startPoint.ch + 1},
                { line: currentLineNumber, ch: endPoint.ch + 5}
            );
        }
    }
    cm.focus();
}

/**
 * 段落引用
 * @param editor MarkMeditor
 */
function toggleBlockquote(editor) {
    var cm = editor.codemirror;
    var startPoint = cm.getCursor('start');
    var endPoint = cm.getCursor('end');

    var headLine = cm.getLine(startPoint.line);
    var reg = /^\s*\>/;
    if (reg.test(headLine)) {
        for (var i = startPoint.line; i <= endPoint.line; i++) {
            var text = cm.getLine(i);
            var tmp = text.replace(/^\s*\>/, '').replace(/^\s/, '');
            cm.replaceRange(tmp, {line: i, ch: 0}, {line: i, ch: text.length});
        }
    } else {
        for (var i = startPoint.line; i <= endPoint.line; i++) {
            var text = cm.getLine(i);
            var tmp = '> ' + text;
            cm.replaceRange(tmp, {line: i, ch: 0}, {line: i, ch: text.length});
        }
    }

    cm.focus();
}

/**
 * 数字有序列表
 * @param editor MarkMeditor
 */
function toggleOrderedList(editor) {
    var cm = editor.codemirror;
    var startPoint = cm.getCursor('start');
    var endPoint = cm.getCursor('end');

    var headLine = cm.getLine(startPoint.line);
    var reg = /^\s*\d+\./;
    if (reg.test(headLine)) {
        for (var i = startPoint.line; i <= endPoint.line; i++) {
            var text = cm.getLine(i);
            var tmp = text.replace(/^\s*\d+\./, '').replace(/^\s/, '');
            cm.replaceRange(tmp, {line: i, ch: 0}, {line: i, ch: text.length});
        }
    } else {
        var index = 1;
        for (var i = startPoint.line; i <= endPoint.line; i++) {
            var text = cm.getLine(i);
            var tmp = (index++) + '. ' + text;
            cm.replaceRange(tmp, {line: i, ch: 0}, {line: i, ch: text.length});
        }
    }

    cm.focus();
}

/**
 * 无序列表
 * @param editor MarkMeditor
 */
function toggleUnorderedList(editor) {
    var cm = editor.codemirror;
    var startPoint = cm.getCursor('start');
    var endPoint = cm.getCursor('end');

    var headLine = cm.getLine(startPoint.line);
    var reg = /^\s*\-/;
    if (reg.test(headLine)) {
        for (var i = startPoint.line; i <= endPoint.line; i++) {
            var text = cm.getLine(i);
            var tmp = text.replace(/^\s*\-/, '').replace(/^\s/, '');
            cm.replaceRange(tmp, {line: i, ch: 0}, {line: i, ch: text.length});
        }
    } else {
        for (var i = startPoint.line; i <= endPoint.line; i++) {
            var text = cm.getLine(i);
            var tmp = '- ' + text;
            cm.replaceRange(tmp, {line: i, ch: 0}, {line: i, ch: text.length});
        }
    }

    cm.focus();
}

/**
 * 添加图片
 * @param editor MarkMeditor
 */
function toggleImage(editor) {
    var _this = editor;
    var cm = editor.codemirror;
    var _fileName = '图片描述';
    var imgLink = "图片连接";

    function insertPic() {
        var startCursor = cm.getCursor('from');    //光标位置
        var endCursor = cm.getCursor('to');    //选择结束位置
        var selectText = cm.getSelection() || _fileName;    //当前选中的文本

        var replaceText = '![' + selectText + ']' + '(' + imgLink + ')';
        
        // 插入text
        cm.replaceSelection(replaceText);

        oceModal('hide');
        cm.focus();
    }
    oceModal({
        title: '插入图片',
        content: '<ul class="nav nav-tabs" role="tablist">\
                    <li class="nav-item">\
                        <a class="nav-link active" id="localTab" data-toggle="tab" href="#localPic" role="tab" aria-controls="localPic" aria-selected="true">本地上传</a>\
                    </li>\
                    <li class="nav-item">\
                        <a class="nav-link" id="remoteTab" data-toggle="tab" href="#remotePic" role="tab" aria-controls="remotePic" aria-selected="false">远程地址获取</a>\
                    </li>\
                </ul>\
                <div class="tab-content">\
                    <div class="tab-pane fade mt-3 show active" id="localPic" role="tabpanel" aria-labelledby="localTab">\
                        <span class="text-muted">图片体积不得大于 4 MB</span>\
                        <br>\
                        <div class="widget-upload form-group">\
                            <input type="file" id="editorUpload" name="image" class="widget-upload__file">\
                            <div class="row">\
                                <div class="col-sm-8">\
                                    <input type="text" id="fileName" class="form-control widget-upload__text" placeholder="拖动图片到这里" readonly/>\
                                </div>\
                                <div class="col-sm-4">\
                                    <a href="javascript:void(0);" class="btn btn-light">选择图片</a>\
                                </div>\
                            </div>\
                        </div>\
                    </div>\
                    <div class="tab-pane fade mt-3" id="remotePic" role="tabpanel" aria-labelledby="remoteTab">\
                        <input type="url" name="img" id="remotePicUrl" class="form-control" placeholder="请输入图片所在网址">\
                    </div>\
                </div>',
        closeText: '取消',
        doneText: '插入',
        show: function() {
            $('#editorUpload').fileUpload({
                url: _this.options.uploadPath,
                type: 'POST',
                dataType: 'json',
                beforeSend: function() {
                    // 下面这句神奇的话会影响到拖动图片上传的成功与否，有待研究
                    // 这句话在拖动图片上传时会报错，但是不影响功能。任何修改都有很大可能导致此功能失败。
                    _fileName = $('#editorUpload').val().split('akepath')[1].slice(1);
                    $('#fileName').val(_fileName).addClass('loading');
                    $('.done-btn').attr('disabled', 'disabled');
                },
                complete: function () {
                    $('#fileName').removeClass('loading');
                    $('.done-btn').attr('disabled', false).click();
                },
                success: function (result) {
                    if(result.code === 0) {
                        imgLink = result.data;
                    } else {
                        alert(result.message);
                    }
                }
            });
        },
        doneFn: function(e) {
            e.preventDefault();
            //远程图片
            if($('#remotePic').hasClass('active') && $('#remotePicUrl').val()) {
                $('#remotePicUrl').addClass('loading');
                $('.done-btn').attr('disabled', 'disabled');
                imgLink = $('#remotePicUrl').val();
                insertPic();
            } else {
                insertPic();
            }
        }
    });
}

/**
 * 标题H2
 * @param editor MarkMeditor
 */
function toggleHeaderLevel2(editor) {
    toggleHeaderLevel(editor, '## ');
}

/**
 * 标题H3
 * @param editor MarkMeditor
 */
function toggleHeaderLevel3(editor) {
    toggleHeaderLevel(editor, '### ');
}

/**
 * 标题H4
 * @param editor MarkMeditor
 */
function toggleHeaderLevel4(editor) {
    toggleHeaderLevel(editor, '#### ');
}

/**
 * 标题H5
 * @param editor MarkMeditor
 */
function toggleHeaderLevel5(editor) {
    toggleHeaderLevel(editor, '##### ');
}

/**
 * 标题H6
 * @param editor MarkMeditor
 */
function toggleHeaderLevel6(editor) {
    toggleHeaderLevel(editor, '###### ');
}

/**
 * 标题
 * @param editor MarkMeditor
 */
function toggleHeaderLevel(editor, start) {
    var cm = editor.codemirror;
    var text = '请输入标题';
    var start = start || '## ';

    var startPoint, endPoint, curPoint;
    var currentLineNumber, startCursor;
    if(cm.getSelection()) {
        // 有选中
        startPoint = cm.getCursor('from');
        endPoint = cm.getCursor('to');
        if (startPoint.line != endPoint.line) {
            return;
        }
        // 行信息
        currentLineNumber = startPoint.line;
        startCursor = startPoint.ch;
    } else {
        //没有选中
        curPoint = cm.getCursor();
        currentLineNumber = curPoint.line;
        startCursor = curPoint.ch;
    }

    var currentLine = cm.getLine(currentLineNumber);
    var replaceLine;
    cm.setSelection({line: currentLineNumber, ch: 0}, {line: currentLineNumber, ch: currentLine.length});


    var reg = /^\s*#/;
    if (reg.test(currentLine)) {
        replaceLine = currentLine.replace(/^\s*#*\s*/, '');
        cm.replaceSelection(replaceLine);
        startPoint = {line: currentLineNumber, ch: 0};
    } else {
        currentLine = currentLine.replace(/^\s*/, '').replace(/^#*\s*/, '');
        replaceLine = start + (currentLine == '' ? text:currentLine);
        cm.replaceSelection(replaceLine);
        startPoint = {line: currentLineNumber, ch: start.length};
    }

    endPoint = {line: currentLineNumber, ch: replaceLine.length};
    
    cm.setSelection(startPoint, endPoint);
    cm.focus();
}

/**
 * 水平分隔符
 * @param editor MarkMeditor
 */
function toggleHorizontalRule(editor) {
    var cm = editor.codemirror;
    var cursor = cm.getCursor();
    cm.replaceRange('\n\n----------\n', cursor);
    cm.setCursor({line: cursor.line + 3, ch: 0});
    cm.focus();
}

/**
 * 撤销
 * @param editor MarkMeditor
 */
function undo(editor) {
    var cm = editor.codemirror;
    cm.undo();
    cm.focus();
}

/**
 * 重做
 * @param editor MarkMeditor
 */
function redo(editor) {
    var cm = editor.codemirror;
    cm.redo();
    cm.focus();
}

/**
 * 帮助
 * @param editor MarkMeditor
 */
function help(editor) {
    var cm = editor;
    muses.open({
        title: '帮助文档',
        content: '对不起，文档还未完善，暂时请<a target="_blank" href="https://www.baidu.com/s?wd=MarkDown">度娘</a>。'
    });
}
